write <src>
Added in 6.4.0.
contract.write is a typed, ergonomic namespace that exposes a contract's
state-changing functions. A write is signed (with the instance's default key, or with a
private key supplied via the account option), broadcast, and resolves to the
transaction id (string).
Read-only (view / pure) functions live in the read namespace instead.
When the ABI is declared as const, function names and argument types are checked at
compile time. This is the recommended alternative to the flat
.send() surface.
Every namespace method is async. All validation (argument count, missing signer,
account, call value, …) surfaces as a promise rejection, never a synchronous throw —
always await the call.
Usage
// Signature (each method on the namespace)
contract.write.<fn>(args: unknown[], options?: WriteOptions): Promise<string> // returns the txID
contract.write.<fn>(options?: WriteOptions): Promise<string> // for a no-argument method
type WriteOptions = {
account?: string; value?: number | bigint; feeLimit?: number;
tokenId?: string; tokenValue?: number; permissionId?: number;
};
const abi = [...] as const;
const contract = tronWeb.contract(abi, 'contractAddress');
// resolves to the transaction id
const txID = await contract.write.methodName([arg0, arg1, ...], options);
The first positional argument is the array of method arguments. For a method that takes no arguments you may pass nothing, or pass the options object directly.
Parameters
| Parameter | Description | Type |
|---|---|---|
| args | Array of the method's arguments, in ABI order. Addresses may be base58 or hex. Omit for a no-arg method. | unknown[] |
| options | Optional. Call options (see below). | object |
write options
| Option | Description | Type |
|---|---|---|
| account | A private key whose derived address owns and signs the transaction — lets you issue the write from a non-default signer (even when the instance has no default key). Must be a 64-hex private key (optional 0x); any other value rejects. Treat it as secret. When omitted, the instance's default key signs. | string |
| value | TRX (in sun) attached to the call (callValue). | number | bigint |
| feeLimit | Maximum energy fee, in sun. Defaults to tronWeb.feeLimit. | number |
| tokenId | TRC10 token id to send with the call. | string |
| tokenValue | TRC10 token amount to send. | number |
| permissionId | Permission id for multi-signature. | number |
The account option is a private key, not an address — its derived address becomes the
transaction owner/signer, so one instance can issue writes from different signers. Keep the
value secret and never expose it client-side. When omitted, the instance's default key signs.
Returns
Promise<string> — the broadcast transaction id.
Selector form (overloads)
Each function is additionally exposed under its full selector, e.g.
contract.write['transfer(address,uint256)']. The selector form pins that exact overload,
so same-arity overloads can be addressed unambiguously:
const txID = await contract.write['transfer(address,uint256)'](['TYYY...', 1], { feeLimit: 90_000_000 });
When two overloads share the same arity, the bare name cannot pick one and the call
rejects with Ambiguous overloaded function "<name>" ... — use the selector key to
disambiguate.
The selector key is a function's canonical signature — its name followed by the
parenthesized input types in canonical form (bare uint / int widen to
uint256 / int256, tuples flatten to (type,…); trcToken is kept as-is per the TVM
convention). Rather than hand-writing it, derive it from an ABI fragment with
tronWeb.utils.abi.buildFunctionSelector — it returns this exact string, the same value
the namespace registers internally:
const fragment = abi.find((f) => f.name === 'transfer');
const key = tronWeb.utils.abi.buildFunctionSelector(fragment); // 'transfer(address,uint256)'
await contract.write[key](['TYYY...', 1], { feeLimit: 90_000_000 });
Namespaces enumerate each method under both its bare name and its selector, so
Object.keys(contract.write) returns two entries per function:
Object.keys(contract.write); // ['transfer', 'transfer(address,uint256)']
Functions named write
If the ABI literally defines a function called write, it stays callable through the
legacy flat surface, and the namespace is served from the same object — so both work:
await contract.write(5).send(); // legacy flat call → txID
await contract.write.write([7], { feeLimit: 90_000_000 }); // namespace entry → txID
Validation & errors
All of the following reject the returned promise:
| Condition | Error message |
|---|---|
| Wrong number of arguments | Contract function "<name>" expects <n> argument(s) but received <m>. |
| No signer configured | Method "<name>" modifies state and requires a signer. Set a private key or default address on the TronWeb instance. |
account is set but is not a valid private key | The "account" option must be a private key. |
| Ambiguous same-arity overload (bare name) | Ambiguous overloaded function "<name>" for the given arguments; pass the full signature ... |
Calling write on a view / pure function | Function "<name>" is read-only. |
Invalid value | call value cannot be negative / call value must be an integer / call value exceeds safe integer range / call value must be a number or bigint |
Example
const abi = [
{
type: 'function',
name: 'transfer',
stateMutability: 'nonpayable',
inputs: [
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [],
},
] as const;
async function demo() {
const contract = tronWeb.contract(abi, 'TContractAddress...');
// signed with the instance's default key; resolves to the txID
const txID = await contract.write.transfer(['TRecipient...', 100], { feeLimit: 100_000_000 });
console.log(txID);
'5e3b3...e0c9'
// …or sign with a specific private key — its derived address owns the transaction
const txID2 = await contract.write.transfer(['TRecipient...', 100], { account: signerPrivateKey, feeLimit: 100_000_000 });
}
demo();